Skip to content

feat: postgres#741

Merged
magne4000 merged 34 commits into
mainfrom
magne4000/postgres
Jun 3, 2026
Merged

feat: postgres#741
magne4000 merged 34 commits into
mainfrom
magne4000/postgres

Conversation

@magne4000

@magne4000 magne4000 commented Jun 2, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added PostgreSQL as a primary database option across all boilerplates
    • Separated ORM/Query Builder selection into its own category
    • Added PostgreSQL service to Docker Compose configurations
  • Bug Fixes

    • Refined database engine selection logic to properly distinguish between databases and ORMs
    • Fixed ORM/Query Builder dependency requirements
  • Chores

    • Added validation rules for PostgreSQL and mutual exclusivity with SQLite
    • Expanded test matrix coverage for database and ORM combinations
    • Updated feature categorization and selection constraints

Review Change Stack

magne4000 and others added 11 commits June 2, 2026 12:39
Add a 'Database engine' category (SQLite default + PostgreSQL) so Postgres
can be chosen standalone or alongside Drizzle/Kysely. Add BatiSet.hasPostgres,
include postgres in hasDatabase, and make hasD1 exclude the Postgres case
(Cloudflare+Postgres talks to Postgres over Workers, not D1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirror the sqlite boilerplate for a raw postgres.js client: db.ts singleton,
example queries + schema/migrate script, $package.json (postgres/dotenv/tsx +
postgres:migrate), $TODO.md (bring-your-own server note), exports/typesVersions,
and nextSteps. Active only when no ORM owns the engine.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
db.ts gains a dbPostgres() branch (drizzle-orm/postgres-js + postgres client);
schema picks pgTable vs sqliteTable at scaffold time; drizzle.config dialect
switches to postgresql; queries bridge dialects via stripped BATI.Any casts;
$package.json swaps better-sqlite3 for postgres when the engine is Postgres.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
db.ts gains dbKyselyPostgres() using kysely-postgres-js' PostgresJSDialect;
the migration creates a serial id on Postgres; migrate.ts and queries select
the Postgres client; $package.json swaps better-sqlite3 for postgres +
kysely-postgres-js. All engines stay typed as Kysely<Database>, so no casts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
db-middleware selects dbPostgres/dbKyselyPostgres/raw pgDb (postgres branches
first so Drizzle/Kysely+Postgres win over the SQLite arms); shared-db env()
emits a postgresql:// DATABASE_URL (compose/dockerfile point at the postgres
service host); +data and the telefunc/trpc/ts-rest/shared-server handlers gain
Postgres arms. Declare @batijs/postgres workspace dep on all importers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…igration

docker-compose.yml gains a healthchecked postgres:17-alpine service, a
postgres_data volume and app.depends_on (service_healthy), all guarded by
BATI.has("postgres"); the sqlite bind volume is now gated on
hasDatabase && !postgres. $Dockerfile.ts runs postgres:migrate at startup for
the standalone client (Drizzle/Kysely already covered by their migrate steps).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rules: postgres requires a Server; postgres conflicts with the raw SQLite tool
and with Prisma; postgres added to the StackBlitz-incompat info. Messages added
to the CLI and website maps. rules.local.spec asserts the new failures.
E2e: a postgres matrix (standalone / drizzle / kysely on Postgres) with a
postgres:migrate beforeAll and todo-persistence + TODO.md assertions; CI starts
a postgres:17 service for --postgres jobs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…no unreachable return

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add Postgres arms to the remaining db-type maps missed in step 5
(telefunc Telefunc.Context + telefunc-handler cast, shared-todo
PageContextServer); give the postgres boilerplate node types so
process.env resolves (postgres.js doesn't pull in @types/node like
better-sqlite3 does).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…late

A YAML comment on the first child of a mapping attaches to the parent node,
so the inline guards on the named-volume entries were never evaluated (both
volumes leaked into output). Keep only the key-level-guarded sqlite_data
volume in the template and append the postgres_data volume from
$docker-compose.yml.ts when postgres is selected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR adds PostgreSQL as a production-ready database option alongside SQLite by introducing a new postgres boilerplate, reorganizing the feature catalog to separate database engines from ORMs, extending Drizzle and Kysely with PostgreSQL adapters, and systematically updating shared infrastructure and RPC frameworks to route data access based on the selected database and ORM combination.

Changes

PostgreSQL Support Infrastructure

Layer / File(s) Summary
PostgreSQL boilerplate package
boilerplates/postgres/bati.config.ts, boilerplates/postgres/files/..., boilerplates/postgres/package.json, boilerplates/postgres/tsconfig.json
New @batijs/postgres boilerplate package with raw postgres.js client support: configuration for raw Postgres selection, package exports and subpath mappings, singleton database connection factory, parameterized SQL query helpers for todos, and schema bootstrap script.
Feature catalog: Database vs ORM separation
packages/features/src/categories.ts, packages/features/src/features.ts
Reorganizes features into Database category (SQLite, PostgreSQL engines) and new ORM / Query builder category (Drizzle, Kysely, Prisma); updates category descriptions and adds PostgreSQL feature entry.
Feature system: helpers, rules, and enums
packages/features/src/helpers.ts, packages/features/src/rules/enum.ts, packages/features/src/rules/rules.ts
Adds #orm and #databases private sets to BatiSet; new public getters hasOrm() and hasDbDemo(); narrows hasD1() to require cloudflare && sqlite only; adds PostgreSQL/SQLite exclusivity rule and ORM database requirement rule.

ORM PostgreSQL Adapters

Layer / File(s) Summary
Drizzle PostgreSQL support
boilerplates/drizzle/files/$package.json.ts, boilerplates/drizzle/files/database/drizzle/db.ts, boilerplates/drizzle/files/database/drizzle/queries/todos.ts, boilerplates/drizzle/files/database/drizzle/schema/todos.ts, boilerplates/drizzle/files/drizzle.config.ts, boilerplates/drizzle/package.json
Adds Drizzle postgres.js adapter: dbPostgres() factory function, conditional todoTable schema (pgTable vs sqliteTable), getAllTodos() query execution logic (raw query vs .all()), dynamic dialect selection in drizzle.config.ts, and conditional postgres dependency.
Kysely PostgreSQL support
boilerplates/kysely/files/$package.json.ts, boilerplates/kysely/files/database/kysely/db.ts, boilerplates/kysely/files/database/kysely/migrate.ts, boilerplates/kysely/files/database/kysely/migrations/001_create_todos_table.ts, boilerplates/kysely/files/database/kysely/queries/todos.ts, boilerplates/kysely/package.json
Adds Kysely PostgreSQL dialect: dbKyselyPostgres() factory, migration provider selection logic, conditional table creation (serial vs autoIncrement()), Bun-aware kysely:migrate script configuration, and conditional postgres/kysely-postgres-js dependencies.
Prisma PostgreSQL support
boilerplates/prisma/bati.config.ts, boilerplates/prisma/files/$TODO.md.ts
Updates Prisma boilerplate to dynamically select datasource provider (postgresql vs sqlite) in bati.config and TODO.md template based on BATI.has("postgres").
D1/SQLite ORM-aware gating
boilerplates/d1-sqlite/bati.config.ts, boilerplates/sqlite/bati.config.ts
Refines D1 and raw SQLite boilerplate selection conditions to require !hasOrm, preventing ORM/raw-client conflicts.

Shared Infrastructure and RPC Frameworks

Layer / File(s) Summary
Shared database middleware and routing
boilerplates/shared-db/bati.config.ts, boilerplates/shared-db/files/server/db-middleware.ts, boilerplates/shared-db/package.json
Updates middleware to import Postgres/Drizzle/Kysely database factories; expands context db type mapping with drizzle+postgres, kysely+postgres, and raw postgres cases; implements multi-branch DB selection logic; switches feature gate from hasDatabase to hasDbDemo.
Shared server create-todo handler
boilerplates/shared-server/files/server/create-todo-handler.ts, boilerplates/shared-server/package.json
Expands handler context typing to include Postgres-specific db type branches; reorganizes insert routing to dispatch to sqlite/kysely/d1/postgres backends based on feature flags.
Shared todo data provider
boilerplates/shared-todo/files/global.d.ts, boilerplates/shared-todo/files/pages/todo/+data.ts, boilerplates/shared-todo/package.json
Adds PostgreSQL provider branch to data hook; refines sqlite/d1 conditions to exclude ORM/D1 overlap; updates global Vike.PageContextServer.db type mapping for Postgres combinations.
tRPC server with Postgres routing
boilerplates/trpc/files/trpc/server.ts, boilerplates/trpc/package.json
Updates context typing and onNewTodo mutation to route inserts to postgres queries; adds Postgres-aware imports and narrowed D1 condition (hasD1 && !hasOrm).
Telefunc server with Postgres routing
boilerplates/telefunc/files/global.d.ts, boilerplates/telefunc/files/pages/todo/TodoList.telefunc.ts, boilerplates/telefunc/files/server/telefunc-handler.ts, boilerplates/telefunc/package.json
Updates context typing, global type mapping, and TodoList handler to support Postgres backend; adds explicit Postgres query imports and refines ORM conditions.
ts-rest handler with Postgres routing
boilerplates/ts-rest/files/server/ts-rest-handler.ts, boilerplates/ts-rest/package.json
Updates platform context typing and createTodo handler to route inserts to postgres with await for async operations.
Server boilerplate template markers
boilerplates/express/files/+server.ts, boilerplates/fastify/files/+server.ts, boilerplates/h3/files/+server.ts, boilerplates/hono/files/server/hono.ts
Updates conditional template markers from hasDatabase to hasDbDemo.

Docker, CI, and Test Infrastructure

Layer / File(s) Summary
Docker Compose PostgreSQL service
boilerplates/docker-compose/env.ts, boilerplates/docker-compose/env.spec.ts, boilerplates/docker-compose/files/docker-compose.yml, boilerplates/docker-compose/files/$Dockerfile.ts
Adds postgres service to docker-compose.yml with healthcheck and dependency management; updates app service volume mounting; pins non-secret env vars to compose values; conditions SQLite/Postgres migration inclusion on !hasOrm.
CI test workflows and matrix
.github/workflows/reusable.run-tests.yml, .github/workflows/tests-entry.yml
Updates reusable workflow to conditionally start PostgreSQL container; reorganizes Ubuntu matrix destinations to separate Solid sqlite/postgres variants and add Prisma/postgres combinations.
Test suite matrix and migration logic
packages/tests/tests/FRAMEWORK+SERVER+DATA.spec.ts, packages/tests/tests/FRAMEWORK+prisma.spec.ts, packages/tests/rules.local.spec.ts
Refactors test matrix to separate engine selection (sqlite vs postgres) from ORM selection; adds postgres migration branch and test assertions; updates Prisma suite to matrix over databases; expands rule validation test cases.

Utilities and User Interface

Layer / File(s) Summary
YAML comment hoisting for guards
packages/core/src/parse/yaml.ts, packages/core/tests/transform-yaml.spec.ts
Adds hoisting helper to relocate leading BATI guard comments from YAML map nodes to first entry keys, enabling proper guard evaluation during transformation.
Package manager detection refactor
packages/core/src/runtime.ts
Refactors pmFromUserAgent to explicitly switch over package manager names and assign exec/run values in switch cases.
Test suite ORM axis
packages/tests-utils/src/suite.ts
Adds orm axis to test suite framework for matrix generation over ORM/query-builder category.
Website UI and rules messaging
website/components/Features.tsx, website/components/FormControl.tsx, website/components/RulesMessages.tsx, packages/cli/rules.ts
Removes conditional "(required)" label rendering; adds PostgreSQL server requirement, PostgreSQL/SQLite mutual exclusivity, and ORM/database requirement messages to rules UI; updates StackBlitz compatibility check.

Possibly related PRs

  • vikejs/bati#689: Both PRs modify the shared-todo data hook in boilerplates/shared-todo/files/pages/todo/+data.ts by updating the conditional backend provider logic for fetching todoItemsInitial.
  • vikejs/bati#738: Main PR's Docker Compose env logic changes in boilerplates/docker-compose/env.ts/env.spec.ts overlap with retrieved PR #738's env variable management refactor for composeEnvEntries behavior.
  • vikejs/bati#734: Both PRs touch .github/workflows/reusable.run-tests.yml and tests-entry.yml to drive docker-based e2e execution—main PR adds conditional PostgreSQL container startup, while retrieved PR restructures the e2e workspace for docker-compose runs.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: postgres' clearly and concisely describes the main change: adding PostgreSQL support throughout the codebase. It directly relates to the primary objective.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 2, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bati c8d62a8 Commit Preview URL

Branch Preview URL
Jun 03 2026, 10:10 AM

magne4000 and others added 17 commits June 2, 2026 15:37
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A comment before the first entry of a block mapping is parsed onto the map
node's commentBefore rather than the first key, so transformYaml never
evaluated it. Hoist it onto the first key so the Pair handler resolves it
like any other entry guard. This lets docker-compose declare the
sqlite_data / postgres_data volumes with plain magic comments again, so the
$docker-compose.yml.ts workaround is reverted. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Remove the unused connectionString parameter from dbPostgres/dbKyselyPostgres
  and the standalone postgres db() (no caller ever passed one; it was
  speculative Cloudflare/Hyperdrive compat that was never wired).
- Drop the JSDoc wall on the standalone client so it mirrors sqlite's db().
- Collapse the duplicated cast in drizzle getAllTodos into one query + a
  folding ternary.
- Remove the BatiSet.hasPostgres getter (a one-use alias for has("postgres"),
  unlike hasD1/hasDatabase which encapsulate derived logic).
- Trim duplicated/over-claiming comments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ategories

Rename the 'Database' category to 'ORM / Query builder' (Drizzle/Kysely/Prisma)
and 'Database engine' to 'Database' (SQLite/PostgreSQL). Move SQLite into the
engine category and drop the invisible sqlite-engine default — the engine is now
an explicit choice. BatiSet derives hasDatabase/hasOrm from the category sets and
hasD1 = cloudflare && sqlite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add ERROR_ORM_R_DATABASE (drizzle/kysely/prisma each require a Database) and
drop ERROR_POSTGRES_X_PRISMA (Prisma + Postgres is now valid). Update the CLI
and website message maps, and rules.local.spec (assert ORM-without-engine and
two-engines fail).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
drizzle/kysely dbSqlite/dbKysely now guard on BATI.has("sqlite") && !hasD1
instead of the implicit !postgres && !hasD1. The raw sqlite/postgres client
boilerplates activate on their engine && !hasOrm (no ORM/query builder owns it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With SQLite now an explicit flag, drizzle/kysely + sqlite also set has("sqlite"),
so the raw-engine arms (sqlite/postgres/D1) must exclude any ORM via !hasOrm —
otherwise drizzle+sqlite would resolve to the raw better-sqlite3 client. Applied
across the db-middleware ternary + context type, the +data branches and the
telefunc/trpc/ts-rest/shared-server handlers (type maps + runtime). shared-db
opts Prisma out of the demo entirely (hasDatabase && !prisma).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prisma now requires an engine; its DATABASE_URL defaults to a postgresql://
string or file:./dev.db per the chosen engine, and 'prisma init' uses the
matching --datasource-provider. Prisma stays demo-less (no Bati todo wiring).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an 'orm' axis. Rework FRAMEWORK+SERVER+DATA to express engine×tool
(sqlite/postgres × drizzle/kysely/raw, D1 via cloudflare+sqlite, both engines
under dokploy) instead of the old implicit-sqlite db list, and reorder beforeAll
to match the tool before the raw-sqlite engine. Pair Prisma with an engine (+server).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
prisma+engine sets the engine flag but has no Bati client, so:
- shared-todo PageContextServer.db augmentation gates on hasDatabase && !prisma
- telefunc-handler db-context map gains a _ fallback (matching the other handlers)
- d1-sqlite (raw D1 queries) gates on hasD1 && !hasOrm, not hasD1 && sqlite,
  so drizzle/kysely + sqlite + cloudflare no longer pull in the raw-D1 client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unify the three context-type augmentations (shared-db, shared-todo, telefunc) to
gate on hasDatabase && !prisma — they describe Bati's managed db, which Prisma
doesn't have. Drops telefunc's now-redundant _ fallback. Handlers keep their _
fallback (they're gated by server/data-lib, so the no-db case is real).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@magne4000 magne4000 marked this pull request as ready for review June 3, 2026 11:13
@magne4000 magne4000 merged commit 9c3dd5f into main Jun 3, 2026
181 of 182 checks passed
@magne4000 magne4000 deleted the magne4000/postgres branch June 3, 2026 11:18
This was referenced Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant